﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace brevis.Nop.SDK
{
    /// <summary>
    /// Represents a "generic" interface factory
    /// </summary>
    /// <typeparam name="TInterface">interface for implementation</typeparam>
    public class InterfaceFactory<TInterface>
    {
        /// <summary>
        /// Returns concrete impl. off an interface class
        /// </summary>
        /// <param name="type">Full qualified type (format: classtype, assemblynam)</param>
        /// <returns></returns>
        /// <remarks>Format: classtype, assemblyname</remarks>
        public TInterface Get(string type)
        {
            var fqt = type.Replace(" ", "").Split(',');

            if (fqt.Length != 2)
                throw new IndexOutOfRangeException(
                    string.Format("Can't create instance of type[{0}]. " +
                                  "Type was not full qualified." +
                                  "Please check provided format: classtype, assemblyname", 
                                  type));

            return Get(fqt[0], fqt[1]);
        }

        /// <summary>
        /// Returns concrete impl. off interface
        /// </summary>
        /// <param name="classtype">Class type</param>
        /// <param name="assemblyname">Assembly name (without file extension .dll)</param>
        /// <returns></returns>
        public TInterface Get(string classtype, string assemblyname)
        {
            /* load assembly */
            var ass = LoadAssembly(assemblyname);

            /* first: check availability of type in ass. */
            var type = ass.GetType(classtype);
            if (type == null)
                throw new Exception(
                    string.Format("classtype [{0}] not found in assembly [{1}].",
                    classtype, assemblyname));

            /* let's go for ultimate job */
            try
            {
                /* create instance and cast to concrete type */
                var instance = (TInterface)ass.CreateInstance(classtype);
                return instance;
            }
            catch (InvalidCastException icerr)
            {
                /* concrete class does not implement abstract class */
                throw new Exception(
                    string.Format("classtype [{0}] is not an impl. of abstract class [{1}]",
                    classtype,
                    typeof(TInterface).ToString()), icerr);
            }
            catch (Exception err)
            {
                /* an other exception was thrown */
                throw new Exception("something else has gone wrong ...", err);
            }

        }

        /// <summary>
        /// Loads an assembly
        /// </summary>
        /// <param name="assemblyname"></param>
        /// <returns></returns>
        private Assembly LoadAssembly(string assemblyname)
        {
            var ass = Assembly.Load(assemblyname);

            if (ass == null)
                throw new NullReferenceException(
                    string.Format("Could not load assembly [{0}]. Please verfiy if file exists in app's binary folder.", 
                    assemblyname));

            return ass;

        }
    }
}
